import { composeParslet, type ParsletFunction } from './Parslet'
import { Parser } from '../Parser'
import { Precedence } from '../Precedence'
import { UnexpectedTypeError } from '../errors'
import { type ObjectResult } from '../result/RootResult'
import { type Grammar } from '../grammars/Grammar'

export function createObjectParslet ({ objectFieldGrammar, allowKeyTypes }: {
  objectFieldGrammar: Grammar
  allowKeyTypes: boolean
}): ParsletFunction {
  return composeParslet({
    name: 'objectParslet',
    accept: type => type === '{',
    parsePrefix: parser => {
      parser.consume('{')
      const result: ObjectResult = {
        type: 'JsdocTypeObject',
        meta: {
          separator: 'comma'
        },
        elements: []
      }

      if (!parser.consume('}')) {
        let separator: 'comma' | 'semicolon' | 'linebreak' | undefined

        const fieldParser = new Parser(objectFieldGrammar, parser.lexer, parser)

        while (true) {
          fieldParser.acceptLexerState(parser)
          let field = fieldParser.parseIntermediateType(Precedence.OBJECT)
          parser.acceptLexerState(fieldParser)

          if (field === undefined && allowKeyTypes) {
            field = parser.parseIntermediateType(Precedence.OBJECT)
          }

          let optional = false
          if (field.type === 'JsdocTypeNullable') {
            optional = true
            field = field.element
          }

          if (field.type === 'JsdocTypeNumber' || field.type === 'JsdocTypeName' || field.type === 'JsdocTypeStringValue') {
            let quote
            if (field.type === 'JsdocTypeStringValue') {
              quote = field.meta.quote
            }

            result.elements.push({
              type: 'JsdocTypeObjectField',
              key: field.value.toString(),
              right: undefined,
              optional,
              readonly: false,
              meta: {
                quote
              }
            })
          } else if (field.type === 'JsdocTypeObjectField' || field.type === 'JsdocTypeJsdocObjectField') {
            result.elements.push(field)
          } else {
            throw new UnexpectedTypeError(field)
          }
          if (parser.lexer.current.startOfLine) {
            separator = 'linebreak'
            // Handle single stray comma/semi-colon
            parser.consume(',') || parser.consume(';')
          } else if (parser.consume(',')) {
            separator = 'comma'
          } else if (parser.consume(';')) {
            separator = 'semicolon'
          } else {
            break
          }
          const type = parser.lexer.current.type
          if (type === '}') {
            break
          }
        }

        result.meta.separator = separator ?? 'comma' // TODO: use undefined here
        if (separator === 'linebreak') {
          // TODO: Consume appropriate whitespace
          result.meta.propertyIndent = '  '
        }

        if (!parser.consume('}')) {
          throw new Error('Unterminated record type. Missing \'}\'')
        }
      }
      return result
    }
  })
}
